/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.semi.fw;

import com.semi.util.FileLogger;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;


/**
 *
 * @author liaoyq
 */
public class ConsoleNioThrd extends Thread {
    private volatile boolean isRun = false;
    private volatile boolean RunSignal = false;
    private final FileLogger mLogger= new FileLogger("com.semi.fw.ConsoleNioThrd");
//休眠间隔
    private long interval=1000l;
    protected CodeCenter mRoot = null;
    protected Selector selector;
    protected ArrayList mClients = new ArrayList();
    protected ByteBuffer mReadBuffer = ByteBuffer.allocate(1024);
    public ConsoleNioThrd(CodeCenter root){
        mRoot = root;
    }
    public boolean isRuning(){
        return isRun;
    }
    /*
    public class SelectorLoop implements Runnable {
        private Selector selector;
        private ByteBuffer temp = ByteBuffer.allocate(1024);
        public SelectorLoop() throws IOException {
            this.selector = Selector.open();
        }
        public Selector getSelector() {
            return this.selector; 
        }
        @Override
        public void run() {
            while(true) {
                try {
                    // 阻塞,只有当至少一个注册的事件发生的时候才会继续.
                    if( this.selector.select()==0 ){
                        Thread.sleep(50);
                        continue;
                    }
                     
                    Set<SelectionKey> selectKeys = this.selector.selectedKeys();
                    Iterator<SelectionKey> it = selectKeys.iterator();
                    while (it.hasNext()) {
                        SelectionKey key = it.next();
                        it.remove();
                        SocketChannel sc = (SocketChannel)key.channel();
                        if( sc.socket().isClosed()||!sc.isOpen()||!key.isValid() ){
                            key.cancel();
                            sc.close();
                            continue;
                        }
                        // 处理事件. 可以用多线程来处理.
                        this.dispatch(key);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }            
        }
        public void dispatch(SelectionKey key) throws IOException, InterruptedException {
            if (key.isAcceptable()) {
                // 这是一个connection accept事件, 并且这个事件是注册在serversocketchannel上的.
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                // 接受一个连接.
                SocketChannel sc = ssc.accept();                 
                // 对新的连接的channel注册read事件. 使用readBell闹钟.
                sc.configureBlocking(false);
                sc.register(selector, SelectionKey.OP_READ);
            } else if (key.isReadable()) {
                // 这是一个read事件,并且这个事件是注册在socketchannel上的.
                SocketChannel sc = (SocketChannel) key.channel();
                // 写数据到buffer
                int count = sc.read(temp);
                if (count < 0) {
                    // 客户端已经断开连接.
                    key.cancel();
                    sc.close();
                    return;
                }
                // 切换buffer到读状态,内部指针归位.
                temp.flip();
                String msg = Charset.forName("UTF-8").decode(temp).toString();
                // echo back.
                sc.write(ByteBuffer.wrap(msg.getBytes(Charset.forName("UTF-8"))));                 
                // 清空buffer
                temp.clear();
            }        
        }
    }; */
    protected void removeClient(SelectionKey key) {
        if( key.attachment()!=null ){
            ((FwUser)key.attachment()).onClose();
            mClients.remove(key.attachment());
        }
        try{
            key.cancel();
            key.channel().close();            
        }catch(IOException e){}
        
    }
    protected void removeClient(FwUser user){
        
            if( user.mChennel.mKey!=null ) user.mChennel.mKey.cancel();
            user.onClose();
            user.mChennel.close();
            mClients.remove(user);
    }
    protected void createClient(SocketChannel sc){
        mLogger.info("client "+ sc.socket().getRemoteSocketAddress()+" up");
        FwUser user = new FwUser(new SocketWrapper(sc),mRoot);
        try{
            sc.configureBlocking(false);
            user.mChennel.mKey = sc.register(selector, SelectionKey.OP_READ,user);
            user.onConnected();
            mClients.add(user);
        }catch(Exception e){
            mLogger.info("client "+sc.socket().getRemoteSocketAddress()+" down",e);
            try{sc.close();}catch(Exception e2){}
        }
    }
    protected void checkTimeout(){
        int dowork=0;
        for(int i=0;i<mClients.size();i++){
            FwUser u =(FwUser)mClients.get(i); 
            if( u.isTimeout()){
                dowork++;
                try{ u.onTimeOut();
                }catch(IOException e){
                 removeClient(u);
                }
            }
        }
        if( dowork<1 )try{
            Thread.sleep(50);
        }catch(Exception e){}
    }
    public void run() { 
        mLogger.info("Thread start......");
        isRun = true;
        RunSignal = true;
        ServerSocketChannel ssc = null;
        try{
            selector = Selector.open();
            ssc = ServerSocketChannel.open();  
            ServerSocket ss = ssc.socket();  
            // set no blocking  
            ssc.configureBlocking(false);             
            ss.bind(new InetSocketAddress(mRoot.ConsolePort()));
            mLogger.info("Listen success on "+mRoot.ConsolePort());
            //ssc.register(selector, SelectionKey.OP_READ);
        }catch(IOException e){
            mLogger.info("Listen failure",e);
            RunSignal = false;
        }
        SocketChannel sc = null;
        try{
        while (RunSignal) {  
        //dosomething
            sc = ssc.accept();
            if( sc!=null )      createClient(sc);            
            if( this.selector.selectNow()==0 ){
                checkTimeout();
                continue;
            }
            Set<SelectionKey> selectKeys = this.selector.selectedKeys();
            Iterator<SelectionKey> it = selectKeys.iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                it.remove();
                FwUser user = (FwUser)key.attachment();
                sc = (SocketChannel)key.channel();
                if( sc.socket().isClosed()|| !sc.isOpen()||!key.isValid() ){
                    removeClient(key);
                    continue;
                }
                // 处理事件. 可以用多线程来处理.
                if( key.isReadable()){
                    try{
                        mReadBuffer.clear();
                        String tmp = user.mChennel.read(mReadBuffer);
                        if ( tmp==null) {
                            // 客户端已经断开连接.
                            removeClient(key);
                            continue;
                        }
                        user.onData(tmp);
                        if( user.IsExit() ) removeClient(key);
                    }catch(IOException e){
                        mLogger.debug("client error",e);
                        removeClient(key);
                    }
                // 清空buffer
                    mReadBuffer.clear();
                }
            }            
        }
        }catch(IOException e){
            mLogger.info("server socket error",e);
        }
        cleanup();
        if( ssc!=null ){
            try {
                ssc.close();
            } catch (IOException ex) {
            }
        }
        mLogger.info("Thread down completed");
        isRun = false;
    }  
    protected void cleanup(){
        for(int i=0;i<mClients.size();i++){
            FwUser user =(FwUser) mClients.get(i);
            if( user.mChennel.isValidate()) {
                user.onClose();
                if( user.mChennel.mKey!=null )user.mChennel.mKey.cancel();
                user.mChennel.close();
            }
        }
        mClients.clear();
        try {
            if( selector!=null ) selector.close();
        } catch (IOException ex) {
        }
    }
    public void closeWait() {  
        mLogger.info("console thread down begin....");
        this.RunSignal = false;
        this.interrupt();
        while( isRun ){ 
            try {
                Thread.sleep(interval);
            } catch (InterruptedException ex) {
            }    
        }
        mLogger.info("console thread down completed");
    }    
}
